#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

#define BACKLOG 5

//子进程读取客户端上送的请求数据
void handleClientData(int newSockfd);

void handler(int sig){
	if(sig == SIGCHLD){
		printf("Child Process is recycle\n");
		wait(NULL);
	}
}

int main(int args, const char * argv[]){

	if(args < 3){
		perror("less args");
		exit(0);
	}

	int sockfd, newSockfd;
	struct sockaddr_in addr;
	//客户端的socket信息 由accept函数填充
	struct sockaddr_in addrRet;
	//表明客户端的socket是什么类型,需要初始化，不然会有问题
	socklen_t addrRetLen = sizeof(struct sockaddr_in);
	pid_t pid;


	//SIGCHLD信号的捕捉函数
	struct sigaction act;
	act.sa_handler = handler;
	//SA_RESTART:使被SIGCHLD信号打断的accept系统调用自动重新发起
	//如果不设置这一行，那么被accept阻塞的主线程，在处理完SIGCHLD信号后，回到accept函数直接报错
	//accept:Interrupted system call
	act.sa_flags = SA_RESTART;
	sigemptyset(&act.sa_mask);
	sigaction(SIGCHLD, &act, NULL);

	//1. 创建套接字：IPV4、TCP
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0){
		perror("socket");
		exit(0);
	}

	//地址快速重用：允许在同一端口上启动新的服务器，即使之前的服务器实例还没有完全关闭
	int flag = 1, len = sizeof(int);
	if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1){
		perror("setsockopt");
		exit(1);
	}

	//2. 绑定通信结构体
	addr.sin_family = AF_INET;
	//atoi:将字符串转化成整形数据
	addr.sin_port = htons(atoi(argv[2]));
#if 1
	if (inet_aton(argv[1], &addr.sin_addr) == 0) {
		fprintf(stderr, "Invalid address\n");
		exit(EXIT_FAILURE);
	}
#else
	addr.sin_addr.s_addr = inet_addr(argv[1]);
#endif
	if(bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == -1){
		perror("bind");
		exit(0);
	}

	//3. 监听套接字
	if(listen(sockfd, BACKLOG) == -1){
		perror("listen");
		exit(0);
	}

	//4. 循环处理客户端发起的连接，生成新的套接字
	while(1){
		newSockfd = accept(sockfd, (struct sockaddr *)&addrRet, &addrRetLen);
		if(newSockfd < 0){
			perror("accept");
			exit(0);
		}
		printf("socket addr: %s, socket port: %d\n", inet_ntoa(addrRet.sin_addr), ntohs(addrRet.sin_port));
		pid = fork();
		if(pid < 0){
			perror("fork");
			exit(0);
		}else if(pid == 0){
			//5.子进程读取客户端上送的请求数据
			//5.1子进程会将父进程的变量复制一份到自己的空间中，子进程无需使用fd，故关闭
			close(sockfd);
			//5.2 读取客户端数据
			handleClientData(newSockfd);
			//5.3 子进程结束时，关闭连接的scoket
			close(newSockfd);
			exit(0);
		}else{
			//父进程
			//关掉当前的socket。该socket只有子进程才需要
			close(newSockfd);
		}
	}

	//6. 关闭套接字
	close(sockfd);
	return 0;
}

//子进程读取客户端上送的请求数据
void handleClientData(int newSockfd){
	int ret;
	// BUFSIZ 8192
	char buf[BUFSIZ] = {0};  
	while(1){
		memset(buf, 0, BUFSIZ);
		ret = read(newSockfd, buf, BUFSIZ);
		if(ret == 0){
			//当把客户端关闭时，返回的就是0,此时一直打印No data,
			//所以后面加上break 退出循环
			printf("No data\n");
			break;
		}else if(ret < 0){
			//出错
			break;
		}else {
			printf("buf : %s", buf);
		}
	}
}
